//
// Created by syshen on 2021/7/7.
//

#ifndef OCR_BASE_DETECT_H
#define OCR_BASE_DETECT_H

#include <iostream>
#include <opencv2/opencv.hpp>
#include "utils/cudaWrapper.h"
#include "utils/ioHelper.h"
#include "TextDetector/base_detect.h"

class BaseDetect {
public:
    BaseDetect(int device_id, bool trt_serialize):device_id(device_id), trt_serialize(trt_serialize) {};
    BaseDetect(BaseDetect& /*other*/) noexcept = 0; // copy constructors
    BaseDetect(BaseDetect&& /*other*/) noexcept = 0; // move constructors
    BaseDetect& operator=(BaseDetect& ) noexcept = 0;
    BaseDetect& operator=(BaseDetect&& ) noexcept = 0;

    virtual int init(const std::string &model_file)=0;

    virtual std::vector <std::vector<cv::Point2f>> extract(const cv::Mat &inputs)=0;


    virtual ~BaseDetect() {};

public:
    static std::map<std::string, BaseDetect*> mapPlugin;

    static std::map<std::string, BaseDetect*>& GetPlugin()
    {
        //局部静态对象，只有在使用到时才会被构造出来，由于是在全局对象构造中被调用到，所以不存在多线程问题，
        //因为这发生在main函数之前，其它线程还没有创建出来

        return mapPlugin;
    }
public:
    int device_id = 0;
    bool trt_serialize;

public:
    virtual bool get_mini_boxes(cv::RotatedRect &rotated_rect, cv::Point2f rect[],
                        int min_size) {

        cv::Point2f temp_rect[4];
        rotated_rect.points(temp_rect);
        for (int i = 0; i < 4; i++) {
            for (int j = i + 1; j < 4; j++) {
                if (temp_rect[i].x > temp_rect[j].x) {
                    cv::Point2f temp;
                    temp = temp_rect[i];
                    temp_rect[i] = temp_rect[j];
                    temp_rect[j] = temp;
                }
            }
        }
        int index0 = 0;
        int index1 = 1;
        int index2 = 2;
        int index3 = 3;
        if (temp_rect[1].y > temp_rect[0].y) {
            index0 = 0;
            index3 = 1;
        } else {
            index0 = 1;
            index3 = 0;
        }
        if (temp_rect[3].y > temp_rect[2].y) {
            index1 = 2;
            index2 = 3;
        } else {
            index1 = 3;
            index2 = 2;
        }

        rect[0] = temp_rect[index0];  // Left top coordinate
        rect[1] = temp_rect[index1];  // Left bottom coordinate
        rect[2] = temp_rect[index2];  // Right bottom coordinate
        rect[3] = temp_rect[index3];  // Right top coordinate

        if (rotated_rect.size.width < min_size ||
            rotated_rect.size.height < min_size) {
            return false;
        } else {
            return true;
        }
    }
};


class TextDetRegistry{
public:

    typedef std::shared_ptr<BaseDetect> (*Creator)(int device_id, bool trt_serialize);

    typedef std::map<std::string, Creator> CreatorRegistry;

    static CreatorRegistry& Registry() {
        static CreatorRegistry* g_registry_ = new CreatorRegistry();
        return *g_registry_;
    }

    static std::shared_ptr<BaseDetect > CreateDetection(const std::string type, int device_id, bool trt_serialize) {
        std::cout << "Creating class " << type << std::endl;
        CreatorRegistry& registry = Registry();
        if (registry.count(type)==1)
            std::cout << "class type " << type << " not registered. " << std::endl;
        return registry[type](device_id, trt_serialize);
    }

    static void AddCreator(const std::string type, Creator creator) {
        CreatorRegistry& registry = Registry();
        if (registry.count(type)==0)
            std::cout << "class type " << type << " already registered.";
        registry[type] = creator;
    }

private:
    TextDetRegistry(){};
};

class TextDetRegisterer {
public:
    TextDetRegisterer(const std::string& type,
                    std::shared_ptr<BaseDetect> (*creator)(int device_id, bool trt_serialize)) {
        std::cout << "Registering class type: " << type << std::endl;
        TextDetRegistry::AddCreator(type, creator);
    }
};

#define REGISTER_TEXT_DETECTION_CREATOR(type, creator)                                  \
  static TextDetRegisterer g_creator_f_##type(#type, creator);                          \

#define REGISTER_TEXT_DETECTION_CLASS(type)                                             \
  std::shared_ptr<BaseDetect> Creator_##type##Class(int device_id, bool trt_serialize)  \
  {                                                                                     \
    return std::shared_ptr<BaseDetect>(new type(device_id, trt_serialize));             \
  }                                                                                     \
  REGISTER_TEXT_DETECTION_CREATOR(type, Creator_##type##Class);


#endif //OCR_BASE_DETECT_H
